package leetcode;
/**
 * 474.一和零
 * 给你⼀个⼆进制字符串数组 strs 和两个整数 m 和 n 。
 * 请你找出并返回 strs 的最⼤⼦集的⼤⼩，该⼦集中最多有 m个0 和 n个1。如果 x的所有元素也是 y的元素，集合 x是集合 y的⼦集 。
 *
 * 输⼊：strs = ["10", "0001", "111001", "1", "0"], m = 5, n = 3
 * 输出：4
 * 解释：最多有 5 个 0 和 3 个 1 的最⼤⼦集是 {"10","0001","1","0"} ，因此答案是 4 。
 * 其他满⾜题意但较⼩的⼦集包括 {"0001","1"} 和 {"10","1","0"} 。{"111001"} 不满⾜题意，因为它含 4
 * 个 1 ，⼤于 n 的值 3 。
 *
 * 输⼊：strs = ["10", "0", "1"], m = 1, n = 1
 * 输出：2
 * 解释：最⼤的⼦集是 {"0", "1"} ，所以答案是 2
 */
public class Num_474 {
    public int findMaxForm(String[] strs, int m, int n) {
        //看起来很像背包问题，m和n是背包的容量，求最多可以放多少个字串（最大价值）？
        //先统计每个子串的0-1数量
        int[][] s = new int[strs.length][2];
        for(int i = 0; i < strs.length; i++){
            s[i][0] = countZero(strs[i]);
            s[i][1] = countOne(strs[i]);
        }
        //三维背包
        int[][][] dp = new int[strs.length + 1][m + 1][n + 1];
        //先遍历元素
        for(int i = 1; i <= strs.length; i++){
            //二遍历容量m
            for(int j = 0; j <= m; j++){
                //最内层遍历容量n
                for(int k = 0; k <= n; k++){
                    //不选第i个：dp[i - 1][j][k]
                    dp[i][j][k] = dp[i - 1][j][k];
                    //选第i个: dp[i - 1][j - s[i-1][0]][k - s[i-1][1]] + 1
                    if(j >= s[i-1][0] && k >= s[i-1][1]){
                        //需要比较包含和不包含两种情况下哪个子集大
                        dp[i][j][k] = Math.max(dp[i - 1][j][k], dp[i - 1][j - s[i-1][0]][k - s[i-1][1]] + 1);
                    }
                }
            }
        }
        return dp[strs.length][m][n];
    }
    //统计字符串中'0'的个数
    private int countZero(String str) {
        int count = 0;
        for(int i = 0; i < str.length(); i++){
            if(str.charAt(i) == '0'){
                count ++;
            }
        }
        return count;
    }
    //统计字符串中'1'的个数
    private int countOne(String str) {
        return str.length() - countZero(str);
    }
}
